﻿using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using NodeBasedSkillEditor.Model;
using NodeBasedSkillEditor.View;

namespace NodeBasedSkillEditor.ViewModel
{
    public class VM_PortGroup : ObservableObject
    {
        private HorizontalAlignment _subPortsHoriAlignment;
        public HorizontalAlignment SubPortsHoriAlignment {
            get { return _subPortsHoriAlignment; }
            set { _subPortsHoriAlignment = value; OnPropertyChanged(); }
        }

        public VM_Node ParentNode { get; private set; }
        public VM_PortCluster ParentCluster => ExpandPort.ParentCluster;
        public VM_Port ExpandPort { get; private set; }
        public PortGroup BindPortGroup { get; private set; }

        public uint Id { get { return ExpandPort.Id; } }
        public uint LocalId { get { return ExpandPort.LocalId; } }
        public int TypeGroupId { get; private set; } = DAGHelper.InvalidTypeGroupId;

        public EPortIOType IOType { get { return ExpandPort.IOType; } }

        public PortGroupControl BindingPortGroupCtrl { get; private set; }
        public UIElementCollection PortCtrlList { get { return BindingPortGroupCtrl.PortStack.Children; } }
        public List<VM_Port> SubPorts = new List<VM_Port>(4);

        //public event Action<int, int, VM_Port> OnSubPortChanged;
        //public event Action<ICtrlAsyncReady> OnCtrlReady;

        public VM_PortGroup(VM_Port expandPortVM, PortGroup portGroupData, PortGroupControl bindCtrl, int typeGroupId = DAGHelper.InvalidTypeGroupId)
        {
            ParentNode = expandPortVM.ParentNode;
            BindPortGroup = portGroupData;
            TypeGroupId = typeGroupId;

            ExpandPort = expandPortVM;
            ExpandPort.IsExpandPort = true;
            ExpandPort.SetParentGroup(this);
            //ParentCluster = expandPortVM.ParentCluster;
            //OnSubPortChanged = onSubPortChanged;

            BindingPortGroupCtrl = bindCtrl;
            InitializeExpandPort();
            foreach (var p in BindPortGroup.Ports)
            {
                IncreasePortForData(p);
            }
        }

        //public void SetParentCluster(VM_PortCluster portClusterVM)
        //{
        //    ParentCluster = portClusterVM;
        //}

        public VM_Port IncreasePort(uint newPortLocalId)
        {
            BasePort newPortData = Activator.CreateInstance(ExpandPort.PortType) as BasePort;
            newPortData.PortLocalId = newPortLocalId;
            return IncreasePort(newPortData);
        }

        public VM_Port IncreasePort(BasePort portData)
        {
            BindPortGroup.Ports.Add(portData);

            return IncreasePortForData(portData);
        }

        /// <summary>
        /// 为了不影响之前的IncreasePortForData代码，包括在多数情况下非必要的Insert替换Add，这里单独写一个函数
        /// </summary>
        /// <param name="portData"></param>
        /// <param name="insertIndex"></param>
        /// <returns></returns>
        public VM_Port InsertPort(BasePort portData, int insertIndex)
        {
            BindPortGroup.Ports.Insert(insertIndex, portData);

            VM_Port newPortVM = new(portData.PortLocalId, ParentNode, ExpandPort.PortName,
                ExpandPort.IOType, portData, ExpandPort.TypeGroupId, ExpandPort.ContextMode, ExpandPort.IsListPort);

            newPortVM.SetParentGroup(this);
            SubPorts.Insert(insertIndex, newPortVM);

            PortControl newPort;
            if (newPortVM.IOType == EPortIOType.Input)
            {
                newPort = new PortControl_Input();
            }
            else
            {
                newPort = new PortControl_Output();
            }
            newPortVM.SetupBindingPortCtrl(newPort);
            OnPortIndexInGroupChanged();

            if (newPort != null)
            {
                PortCtrlList.Insert(insertIndex, newPort);
                ParentNode.OnSubPortOfGroupChanged(TypeGroupId, 1, newPortVM);
                ParentCluster?.OnSubPortOfGroupChanged(1, newPortVM);
                //OnSubPortChanged?.Invoke(TypeGroupId, 1, newPortVM);
                return newPortVM;
            }
            else return null;

        }

        VM_Port IncreasePortForData(BasePort portData)
        {
            VM_Port newPortVM = new(portData.PortLocalId, ParentNode, ExpandPort.PortName,
                ExpandPort.IOType, portData, ExpandPort.TypeGroupId, ExpandPort.ContextMode, ExpandPort.IsListPort);

            newPortVM.SetParentGroup(this);
            SubPorts.Add(newPortVM);

            PortControl newPort;
            if (newPortVM.IOType == EPortIOType.Input)
            {
                newPort = new PortControl_Input();
            }
            else
            {
                newPort = new PortControl_Output();
            }
            newPortVM.SetupBindingPortCtrl(newPort);
            OnPortIndexInGroupChanged();

            if (newPort != null)
            {
                PortCtrlList.Insert(PortCtrlList.Count - 1, newPort);
                //OnSubPortChanged?.Invoke(TypeGroupId, 1, newPortVM);
                ParentNode.OnSubPortOfGroupChanged(TypeGroupId, 1, newPortVM);
                ParentCluster?.OnSubPortOfGroupChanged(1, newPortVM);
                return newPortVM;
            }
            else return null;
        }

        public void DeletePort(VM_Port portVM)
        {
            SubPorts.Remove(portVM);
            PortCtrlList.Remove(portVM.BindingPortCtrl);
            BindPortGroup.Ports.Remove(portVM.BindPortData);
            ParentNode.OnSubPortOfGroupChanged(TypeGroupId, -1, portVM);
            ParentCluster?.OnSubPortOfGroupChanged(-1, portVM);
            //OnSubPortChanged?.Invoke(TypeGroupId, -1, port);
            OnPortIndexInGroupChanged();
        }

        public void ResetAndChangePortsToType(Type destPortType)
        {
            var newExpPortData = Activator.CreateInstance(destPortType) as BasePort;
            newExpPortData.PortLocalId = BindPortGroup.ExpandPortData.PortLocalId;

            BindPortGroup.ExpandPortData = newExpPortData;
            ExpandPort.BindPortData = newExpPortData;

            BindPortGroup.Ports.Clear();
            foreach (var sp in SubPorts)
            {
                var newPortData = Activator.CreateInstance(destPortType) as BasePort;
                newPortData.PortLocalId = sp.LocalId;
                sp.BindPortData = newPortData;
                BindPortGroup.Ports.Add(newPortData);
            }
        }

        void InitializeExpandPort()
        {
            PortControl newPort = null;
            if (ExpandPort.IOType == EPortIOType.Input)
            {
                SubPortsHoriAlignment = HorizontalAlignment.Left;
                newPort = new PortControl_Input();
                PortCtrlList.Add(newPort);
            }
            else if (ExpandPort.IOType == EPortIOType.Output)
            {
                SubPortsHoriAlignment = HorizontalAlignment.Right;
                newPort = new PortControl_Output();
                PortCtrlList.Add(newPort);
            }
            ExpandPort.SetupBindingPortCtrl(newPort);
        }

        void OnPortIndexInGroupChanged()
        {
            foreach (var sp in SubPorts)
            {
                sp.UpdatePortDisplayName();
            }
        }
    }
}
